<template>
    <div class="page">
        <!-- 顶部返回 -->
        <div class="page-header">
            <span class="back" role="button" @click="goBack">返回</span>
            <span class="sep">|</span>
            <span class="title">详情</span>
        </div>

        <a-spin :spinning="loading" tip="页面拼命加载中....">
            <!-- 表单主体 -->
            <a-form class="form" :model="form" layout="horizontal" :label-col="{ flex: '140px' }"
                :wrapper-col="{ flex: 'auto' }" :colon="false" ref="formRef">
                <!-- 基础信息 -->
                <a-form-item label="接口名称：" class="row-40">
                    <a-input v-model:value="form.name" placeholder="请输入" :disabled="true" />
                </a-form-item>

                <a-form-item label="启用状态：" class="row-40">
                    <a-radio-group v-model:value="form.enabled" :disabled="true">
                        <a-radio value="enabled">启用</a-radio>
                        <a-radio value="disabled">停用</a-radio>
                    </a-radio-group>
                </a-form-item>

                <a-form-item label="开放状态：" class="row-40">
                    <a-radio-group v-model:value="form.public" :disabled="true">
                        <a-radio value="public">公开</a-radio>
                        <a-radio value="private">私密</a-radio>
                    </a-radio-group>
                </a-form-item>

                <!-- 字典选择 -->
                <a-form-item label="接口类型：" class="row-40">
                    <a-select v-model:value="form.apiType" placeholder="请选择" allow-clear :loading="dictLoading.apiType"
                        :disabled="true">
                        <a-select-option v-for="opt in apiTypeOptions" :key="opt.value" :value="opt.value">{{ opt.label
                            }}</a-select-option>
                    </a-select>
                </a-form-item>

                <a-form-item label="输入类型：" class="row-40">
                    <a-select v-model:value="form.inputType" placeholder="请选择" allow-clear
                        :loading="dictLoading.inputType" :disabled="true">
                        <a-select-option v-for="opt in inputTypeOptions" :key="opt.value" :value="opt.value">{{
                            opt.label }}</a-select-option>
                    </a-select>
                </a-form-item>

                <a-form-item label="数据类型：" class="row-40">
                    <a-select v-model:value="form.dataType" placeholder="请选择" allow-clear
                        :loading="dictLoading.dataType" :disabled="true">
                        <a-select-option v-for="opt in dataTypeOptions" :key="opt.value" :value="opt.value">{{ opt.label
                            }}</a-select-option>
                    </a-select>
                </a-form-item>

                <!-- 索引关联 -->
                <a-form-item label="索引关联：" class="row-40">
                    <a-space>
                        <a-button :disabled="true">选择</a-button>
                    </a-space>
                </a-form-item>

                <!-- 索引映射表 -->
                <div v-if="indexLinked" class="index-table-card">
                    <div class="index-table-head">
                        <span class="name">{{ form.indexBinding || '（已选索引）' }}</span>
                        <a-tooltip title="移除">
                            <CloseOutlined class="close" />
                        </a-tooltip>
                    </div>

                    <a-table :data-source="mappingRows" :columns="mappingColumns" row-key="key" bordered
                        :pagination="false" class="mapping-table">
                        <template #bodyCell="{ column, record }">
                            <template v-if="column.dataIndex === 'searchField'">
                                <div class="cell-stretch">
                                    <a-input v-model:value="record.searchField" placeholder="请输入" :disabled="true" />
                                </div>
                            </template>

                            <template v-else-if="column.dataIndex === 'searchMode'">
                                <div class="cell-stretch">
                                    <a-select v-model:value="record.searchMode" placeholder="请选择（单选）"
                                        :loading="searchModeLoading" :disabled="true" :dropdownMatchSelectWidth="false">
                                        <a-select-option v-for="opt in searchModeOptions" :key="opt.value"
                                            :value="opt.value">{{ opt.label }}</a-select-option>
                                    </a-select>
                                </div>
                            </template>

                            <template v-else-if="column.dataIndex === 'indexField'">
                                <div class="cell-stretch">
                                    <a-select v-if="indexFieldOptionObjects.length"
                                        v-model:value="record.indexFieldList" mode="multiple"
                                        :options="indexFieldOptionObjects" placeholder="选择索引字段" class="multi-wrap"
                                        :allow-clear="false" :show-arrow="true" :dropdownMatchSelectWidth="false"
                                        :listHeight="320" :tagRender="renderTag" :showSearch="true"
                                        :filterOption="filterOption" :disabled="true" />
                                    <div v-else class="empty-select">
                                        <span class="tip">暂无可选字段</span>
                                        <a-button type="link" size="small" :disabled="true">刷新</a-button>
                                    </div>
                                </div>
                            </template>

                            <template v-else-if="column.key === 'ops'">
                                <a-button class="del-btn" size="small" type="text" danger :disabled="true">删除</a-button>
                            </template>
                        </template>
                    </a-table>

                    <div class="index-add-row">
                        <div class="add-btn" style="opacity:.5;cursor:not-allowed;">增加行</div>
                    </div>
                </div>

                <!-- 分页参数 -->
                <a-form-item label="Size：" class="row-40">
                    <a-input-number v-model:value="form.page_size" :min="1" :precision="0" style="width: 50%"
                        placeholder="每页条数，建议10" :disabled="true" />
                </a-form-item>

                <a-form-item label="Page：" class="row-40">
                    <a-input-number v-model:value="form.max_pages" :min="1" :precision="0" style="width: 50%"
                        placeholder="最大页数，建议51" :disabled="true" />
                </a-form-item>

                <!-- 算法配置 -->
                <a-form-item label="算法配置：" class="row-40">
                    <a-button :disabled="true">选择</a-button>
                </a-form-item>

                <div v-if="algoList.length" class="algo-picked-wrap">
                    <div v-for="(row, i) in algoList" :key="row.name" class="algo-picked-row">
                        <div class="algo-name">{{ row.name }}</div>
                        <a-select v-model:value="row.version"
                            :options="versionOptionsMap[row.name] || algoVersionOptions" :disabled="true" />
                        <a-button class="del-btn" size="small" type="text" danger :disabled="true">删除</a-button>
                    </div>
                </div>

                <!-- 词库配置 -->
                <a-form-item label="词库配置：" class="row-40">
                    <a-button :disabled="true">选择</a-button>
                </a-form-item>

                <div v-if="lexiconList.length" class="lexicon-chips">
                    <span v-for="it in lexiconList" :key="it" class="chip">
                        {{ it }}
                    </span>
                </div>

                <!-- 向量搜索配置 -->
                <a-form-item label="向量搜索配置：" class="row-40">
                    <a-space>
                        <!-- 启用 -->
                        <a-button type="primary" :ghost="form.vectorEnabled !== true" :disabled="true"
                            :class="['vector-toggle', form.vectorEnabled ? 'active-on' : '']">启用</a-button>

                        <!-- 停用 -->
                        <a-button type="primary" :ghost="form.vectorEnabled !== false" :disabled="true"
                            :class="['vector-toggle', !form.vectorEnabled ? 'active-off' : '']">停用</a-button>
                    </a-space>
                </a-form-item>

                <div class="vector-card" v-show="form.vectorEnabled">
                    <a-form-item label="*召回算法：" class="row-40 inner">
                        <a-select v-model:value="form.recallMetric" style="width: 280px"
                            :loading="recallLoading.algorithm" :disabled="true" placeholder="请选择">
                            <a-select-option v-for="opt in recallAlgorithmOptions" :key="opt.value"
                                :value="opt.value">{{ opt.value ?
                                    opt.label : '' }}</a-select-option>
                        </a-select>
                    </a-form-item>

                    <a-form-item label="*召回阈值：" class="row-40 inner">
                        <a-select v-model:value="form.recallDim" style="width: 280px" :loading="recallLoading.threshold"
                            :disabled="true" placeholder="请选择">
                            <a-select-option v-for="opt in recallThresholdOptions" :key="opt.value"
                                :value="opt.value">{{ opt.label
                                }}</a-select-option>
                        </a-select>
                    </a-form-item>
                </div>

                <!-- 接口地址 -->
                <a-form-item label="接口地址：" class="row-40">
                    <div class="api-card">
                        <div class="api-row" v-for="(m, i) in apiEndpointMetas" :key="i">
                            <code class="api-text"><strong>{{ m.method }}</strong>&nbsp;{{ m.url }}</code>
                            <a-space size="small" class="api-ops">
                                <a-button type="link" size="small" :disabled="true">测试</a-button>
                                <a-button type="link" size="small" :disabled="true">复制</a-button>
                            </a-space>
                        </div>
                    </div>
                </a-form-item>
            </a-form>
        </a-spin>

        <!-- 底部操作 -->
        <div class="page-actions">
            <a-button type="primary" :disabled="true">保存</a-button>
            <a-button type="primary" @click="goBack">返回</a-button>
        </div>

        <!-- 下方三个弹窗 -->
        <a-modal v-model:visible="indexModalOpen" :title="null" :footer="null" :closable="false" width="560px" centered
            class="index-modal modal-narrow">
            <div class="im-head">
                <div class="im-title">索引列表</div>
                <a-button type="link" size="small" @click="indexModalOpen = false">关闭</a-button>
            </div>
            <a-radio-group v-model:value="indexModalSelectedId" class="im-list">
                <a-radio v-for="it in indexModalOptions" :key="it.id" :value="it.id">{{ it.name }}</a-radio>
            </a-radio-group>
            <div class="im-footer">
                <a-space>
                    <a-button type="primary" :disabled="true">确定</a-button>
                    <a-button @click="indexModalOpen = false">取消</a-button>
                </a-space>
            </div>
        </a-modal>

        <a-modal v-model:visible="algoModalOpen" :title="null" :footer="null" :closable="false" width="480px" centered
            class="algo-modal modal-narrow">
            <div class="im-head">
                <div class="im-title">算法模型列表</div>
                <a-button type="link" size="small" @click="algoModalOpen = false">关闭</a-button>
            </div>
            <a-checkbox-group v-model:value="algoModalChecked" class="algo-list">
                <a-checkbox v-for="it in algoCandidates" :key="it.code" :value="it.name">{{ it.name }}</a-checkbox>
            </a-checkbox-group>
            <div class="im-footer">
                <a-space>
                    <a-button type="primary" :disabled="true">确定</a-button>
                    <a-button @click="algoModalOpen = false">取消</a-button>
                </a-space>
            </div>
        </a-modal>

        <a-modal v-model:visible="lexiconModalOpen" :title="null" :footer="null" :closable="false" width="440px"
            centered class="lexicon-modal modal-narrow">
            <div class="im-head">
                <div class="im-title">词库列表</div>
                <a-button type="link" size="small" @click="lexiconModalOpen = false">关闭</a-button>
            </div>
            <a-checkbox-group v-model:value="lexiconModalChecked" class="lexicon-list">
                <a-checkbox v-for="it in lexiconCandidates" :key="it.code" :value="it.code">{{ it.value }}</a-checkbox>
            </a-checkbox-group>
            <div class="im-footer">
                <a-space>
                    <a-button type="primary" :disabled="true">确定</a-button>
                    <a-button @click="lexiconModalOpen = false">取消</a-button>
                </a-space>
            </div>
        </a-modal>

        <!-- 接口预览弹窗 -->
        <a-modal v-model:visible="previewOpen" title="接口预览" :footer="null" :closable="true" width="820px" centered
            :bodyStyle="{ overflow: 'hidden' }" class="modal-narrow">
            <div class="api-card api-card-preview">
                <div class="api-row-block" v-for="(row, i) in previewList" :key="i">
                    <div class="api-type-line">
                        <span class="label">接口类型：</span>
                        <a-tag>{{ row.interface_method || '-' }}</a-tag>
                    </div>
                    <div class="api-row">
                        <code class="api-text">{{ row.interface_url }}</code>
                    </div>
                    <div class="api-params" v-if="row.params && Object.keys(row.params).length">
                        <div class="label">接口参数：</div>
                        <pre class="params-pre">{{ formatParams(row.params) }}</pre>
                    </div>
                </div>
            </div>
        </a-modal>
        <div style="width: 100%; height: 10vh; background: transparent;"></div>
    </div>
</template>

<script setup lang="ts">
import { reactive, computed, watch, onMounted, ref, h, nextTick } from 'vue'
import { message, Tag, Tooltip } from 'ant-design-vue'
import { useRouter, useRoute } from 'vue-router'
import { CloseOutlined } from '@ant-design/icons-vue'
import type { Rule } from 'ant-design-vue'
import api from '@/api'

const router = useRouter()
const route = useRoute()
const formRef = ref()
const loading = ref(false)

type Option = { label: string; value: string }
type AlgoPicked = { name: string; version: string }
type MappingRow = { key: string; searchField: string; searchMode: string; indexField: string; indexFieldList: string[]; _editable?: boolean }

const apiTypeOptions = ref<Option[]>([])
const inputTypeOptions = ref<Option[]>([])
const dataTypeOptions = ref<Option[]>([])
const dictLoading = reactive({ apiType: false, inputType: false, dataType: false })
const recallAlgorithmOptions = ref<Option[]>([])
const recallThresholdOptions = ref<Option[]>([])
const recallLoading = reactive({ algorithm: false, threshold: false })

const searchModeLoading = ref(false)
const searchModeOptions = ref<Option[]>([])

const algoCandidates = ref<Array<{ code: string; name: string }>>([])
const algoListRef = ref<AlgoPicked[]>([])
const algoList = computed(() => algoListRef.value)
const versionOptionsMap = ref<Record<string, Option[]>>({})
const algoVersionOptions = [
    { label: '默认算法 v1.0.0.13', value: '默认算法 v1.0.0.13' },
    { label: '默认算法 v1.0.0.12', value: '默认算法 v1.0.0.12' },
    { label: '默认算法 v1.0.0.11', value: '默认算法 v1.0.0.11' },
    { label: '默认算法 v1.0.0.10', value: '默认算法 v1.0.0.10' }
]
function extractVersionCode(versionName: string) {
    const m = versionName.match(/(v\d+\.\d+\.\d+\.\d+)/i)
    return m ? m[1] : versionName
}

const lexiconCodePicked = ref<string[]>([])
const lexiconListRef = ref<string[]>([])
const lexiconList = computed(() => lexiconListRef.value)

const indexFieldOptions = ref<string[]>([])
const indexFieldOptionObjects = computed(() => indexFieldOptions.value.map(v => ({ label: v, value: v })))

/** 接口地址 */
const apiEndpoints = ref<string[]>([])
const apiEndpointMetas = ref<Array<{ display: string; method: string; url: string; params: Record<string, any> }>>([])

// 表单
const form = reactive({
    id: '' as string | number,
    name: '',
    enabled: 'enabled',
    public: 'public',
    apiType: undefined as string | undefined,
    inputType: undefined as string | undefined,
    dataType: undefined as string | undefined,
    indexBinding: '' as string,
    page_size: undefined as number | undefined,
    max_pages: undefined as number | undefined,
    size: undefined as number | undefined,
    page: undefined as number | undefined,
    vectorEnabled: true,
    recallMetric: '' as string | undefined,
    recallDim: '' as string | undefined,
    algoList: [] as { name: string; version: string }[],
    lexiconList: [] as string[],
    apiList: [] as string[]
})

// 索引映射表
const mappingRows = ref<MappingRow[]>([])
const mappingColumns = [
    { title: '搜索字段', dataIndex: 'searchField', width: 240 },
    { title: '搜索方式（单选）', dataIndex: 'searchMode', width: 280 },
    { title: '索引字段', dataIndex: 'indexField', width: 560 },
    { title: '操作', key: 'ops', width: 90 }
]

// 工具渲染
function renderTag(props: any) {
    const { label, closable, onClose } = props
    return h(Tooltip, { title: String(label) }, {
        default: () => h(Tag, {
            closable, onClose,
            style: 'max-width:180px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; margin-right:4px;'
        }, () => String(label))
    })
}
function filterOption(input: string, option: any) {
    const txt = String(option?.label ?? option?.value ?? '').toLowerCase()
    return txt.includes(input.toLowerCase())
}
function getDefaultSearchModeValue() { return searchModeOptions.value.length ? String(searchModeOptions.value[0].value) : '' }

const isEdit = computed(() => true)
const indexLinked = computed(() => !!(form.indexBinding))

// 字典拉取
function mapDict(res: any): any[] {
    const d = res?.data
    if (Array.isArray(d) && d.length > 0) {
        if (typeof d[0] === 'string') return d.map((s: string) => ({ label: s, value: s }))
        return d.map((it: any) => ({ label: String(it?.value ?? it?.label ?? it?.code ?? ''), value: String(it?.code ?? it?.value ?? '') }))
    }
    return []
}
function mapDictSearchMode(res: any): any[] {
    const d = res?.data
    if (Array.isArray(d) && d.length > 0) {
        if (typeof d[0] === 'string') {
            return d.map((s: string) => {
                const codeGuess = s === '关键词' ? 'keyword' : s === '短语' ? 'phrase' : s.toLowerCase()
                return { label: s, value: codeGuess }
            })
        }
        return d.map((it: any) => ({ label: String(it?.value ?? it?.label ?? ''), value: String(it?.code ?? it?.value ?? '') }))
    }
    return []
}
async function loadApiTypeOptions() {
    dictLoading.apiType = true
    try { apiTypeOptions.value = mapDict(await (api as any).dictionaryInterfaceType()) }
    catch { apiTypeOptions.value = [] }
    finally { dictLoading.apiType = false }
}
async function loadInputTypeOptions() {
    dictLoading.inputType = true
    try { inputTypeOptions.value = mapDict(await (api as any).dictionaryInputType()) }
    catch { inputTypeOptions.value = [] }
    finally { dictLoading.inputType = false }
}
async function loadDataTypeOptions() {
    dictLoading.dataType = true
    try { dataTypeOptions.value = mapDict(await (api as any).dictionaryDataType()) }
    catch { dataTypeOptions.value = [] }
    finally { dictLoading.dataType = false }
}
async function loadSearchModeOptions() {
    searchModeLoading.value = true
    try { searchModeOptions.value = mapDictSearchMode(await (api as any).dictionarySearchMode()) }
    catch { searchModeOptions.value = [] }
    finally { searchModeLoading.value = false }
}
async function loadRecallAlgorithmOptions() {
    recallLoading.algorithm = true
    try {
        recallAlgorithmOptions.value = mapDict(await (api as any).dictionaryRecallAlgorithm())
        if (!form.recallMetric && recallAlgorithmOptions.value.length) form.recallMetric = String(recallAlgorithmOptions.value[0].value)
    } catch { recallAlgorithmOptions.value = [] }
    finally { recallLoading.algorithm = false }
}
async function loadRecallThresholdOptions() {
    recallLoading.threshold = true
    try {
        recallThresholdOptions.value = mapDict(await (api as any).dictionaryRecallThreshold())
        if (!form.recallDim && recallThresholdOptions.value.length) form.recallDim = String(recallThresholdOptions.value[0].value)
    } catch { recallThresholdOptions.value = [] }
    finally { recallLoading.threshold = false }
}
async function loadAllDictionaries() {
    await Promise.all([loadApiTypeOptions(), loadInputTypeOptions(), loadDataTypeOptions(), loadRecallAlgorithmOptions(), loadRecallThresholdOptions(), loadSearchModeOptions()])
}

// 根据索引ID设置索引名称
async function setIndexNameById(id: string | number) {
    try {
        const res: any = await (api as any).getIndexList()
        const list = Array.isArray(res?.data) ? res.data : []
        const found = list.find((it: any) => String(it?.index_id) === String(id))
        form.indexBinding = found?.index_name ? String(found.index_name) : ''
    } catch {
        form.indexBinding = ''
    }
}

// 详情加载
async function loadDetail(id: string | number) {
    loading.value = true
    try {
        const res: any = await (api as any).getSearchInterfaceById({ id })
        const detail = res?.data ?? res
        if (!detail || typeof detail !== 'object') throw new Error('未获取到有效详情')

        // 基础信息
        form.id = detail.id ?? id
        form.name = String(detail.interface_name ?? '')
        form.enabled = detail.interface_enable ? 'enabled' : 'disabled'
        form.public = String(detail.interface_open ?? 'public')
        form.apiType = String(detail.interface_type ?? '')
        form.inputType = String(detail.input_type ?? '')
        form.dataType = String(detail.data_type ?? '')

        // 分页
        const ps = detail.page_size ?? detail.size
        const mp = detail.max_pages ?? detail.page
        form.page_size = ps != null ? Number(ps) : undefined
        form.max_pages = mp != null ? Number(mp) : undefined
        form.size = form.page_size as any
        form.page = form.max_pages as any

        // 词库
        if (Array.isArray(detail.dictionary_config)) {
            lexiconCodePicked.value = [...detail.dictionary_config]
            lexiconListRef.value = [...detail.dictionary_config]
            form.lexiconList = [...lexiconListRef.value]
        } else {
            lexiconCodePicked.value = []; lexiconListRef.value = []; form.lexiconList = []
        }

        // 算法
        if (Array.isArray(detail.algorithm_config)) {
            algoListRef.value = detail.algorithm_config
                .map((it: any) => ({ name: String(it?.algorithm_name || ''), version: String(it?.algorithm_version_name || '') }))
                .filter(a => a.name)
            form.algoList = [...algoListRef.value]
        } else {
            algoListRef.value = []; form.algoList = []
        }

        // 向量/召回
        form.vectorEnabled = !!detail.vector_search_config
        form.recallMetric = detail.recall_algorithm ? String(detail.recall_algorithm) : ''
        form.recallDim = detail.recall_threshold ? String(detail.recall_threshold) : ''

        // 索引关联
        if (detail.index_rel !== undefined && detail.index_rel !== null) {
            await setIndexNameById(detail.index_rel)
            await loadIndexFieldOptions(detail.index_rel as string | number)
        } else {
            form.indexBinding = ''
        }

        // 索引映射
        if (Array.isArray(detail.index_list) && detail.index_list.length) {
            const def = getDefaultSearchModeValue()
            mappingRows.value = detail.index_list.map((r: any, idx: number) => {
                const raw = r?.index_field ?? r?.indexField ?? ''
                const list = Array.isArray(raw) ? raw : String(raw).split(',').map((s: string) => s.trim()).filter(Boolean)
                let mode = String(r?.search_mode ?? r?.searchMode ?? '').trim()
                const hit = searchModeOptions.value.find(o => o.value === mode || o.label === mode || o.value === mode.toLowerCase())
                if (hit) mode = hit.value
                else if (!mode) mode = def
                return { key: `row_${idx + 1}`, searchField: r?.search_field ?? r?.searchField ?? '', searchMode: mode, indexField: Array.isArray(raw) ? raw.join(',') : String(raw), indexFieldList: list }
            })
        } else {
            mappingRows.value = []
        }

        /** ===== 接口地址 ===== */
        const listRaw = Array.isArray(detail?.interface_url_list)
            ? detail.interface_url_list
            : (detail?.interface_url || detail?.interface_method || detail?.test_params)
                ? [{ interface_url: detail.interface_url, interface_method: detail.interface_method, test_params: detail.test_params }]
                : []

        apiEndpointMetas.value = (listRaw || []).map((it: any) => {
            const method = String(it?.interface_method || it?.method || '').toUpperCase()
            const url = String(it?.interface_url || it?.url || '')
            const params = (it?.test_params || it?.params || {}) as Record<string, any>
            const display = `${method.padEnd(4, ' ')} ${url}`
            return { display, method, url, params }
        }).filter(x => x.method && x.url)

        if (apiEndpointMetas.value.length === 0) {
            apiEndpoints.value = [
                'GET  /api/news/search?keyword={user_keyword}&page={page_number}&size={results_per_page}',
                'POST /api/news/search?keyword={user_keyword}&page={page_number}&size={results_per_page}'
            ]
            form.apiList = [...apiEndpoints.value]
        } else {
            apiEndpoints.value = apiEndpointMetas.value.map(m => m.display)
            form.apiList = [...apiEndpoints.value]
        }
        /** 接口地址 */

    } catch (e: any) {
        message.error(e?.message || '加载详情失败')
    } finally {
        loading.value = false
    }
}

// 读取某索引的字段列表
async function loadIndexFieldOptions(id: string | number) {
    try {
        const res = await (api as any).getIndexFieldListById({ id })
        const arr = Array.isArray(res?.data) ? res.data : Array.isArray(res?.data?.list) ? res.data.list : []
        const names = (arr || [])
            .map((s: any) => String(typeof s === 'string' ? s : s?.field_name ?? s?.name ?? s?.value ?? '').trim())
            .filter(Boolean)
        indexFieldOptions.value = Array.from(new Set(names))
    } catch {
        indexFieldOptions.value = []
    }
}

// 返回
function goBack() { router.back() }

// 初始化
onMounted(async () => {
    await loadAllDictionaries()
    const id = (route.query.id as string) || (route.params?.id as string) || ''
    if (!id) { message.warning('缺少详情ID，已返回'); router.back(); return }
    await loadDetail(id)
})

// 预览弹窗相关
const previewOpen = ref(false)
const previewList = ref<Array<{ interface_url: string; interface_method: string; params?: Record<string, any> }>>([])
function formatParams(p: Record<string, any>) { try { return JSON.stringify(p, null, 2) } catch { return String(p) } }

// 弹窗占位
const indexModalOpen = ref(false)
const indexModalSelectedId = ref<string | number | null>(null)
const indexModalOptions = ref<Array<{ id: string | number; name: string }>>([])
const algoModalOpen = ref(false)
const algoModalChecked = ref<string[]>([])
const lexiconModalOpen = ref(false)
const lexiconCandidates = ref<Array<{ code: string; value: string }>>([])

</script>


<style lang="less" scoped>
.page {
    padding: 8px 12px 24px 12px;
    background: #fff;
    height: 100vh;
    overflow: auto;
    -webkit-overflow-scrolling: touch;
    scroll-behavior: smooth;
    overscroll-behavior: contain;
    scrollbar-gutter: stable both-edges;
    scrollbar-width: thin;

    scrollbar-color: #c7d1e6 #f2f4f8;

    &::-webkit-scrollbar {
        width: 10px;
        height: 10px;
    }

    &::-webkit-scrollbar-track {
        background: #f0f3ff;
        border-radius: 8px;
        box-shadow: inset 0 0 2px rgba(0, 0, 0, .05);
    }

    &::-webkit-scrollbar-thumb {
        background: linear-gradient(180deg, #c7d2fe, #a5b4fc);
        border-radius: 8px;
        border: 2px solid #f0f3ff;
    }

    &::-webkit-scrollbar-thumb:hover {
        background: linear-gradient(180deg, #a5b4fc, #818cf8);
    }

    &::-webkit-scrollbar-corner {
        background: transparent;
    }
}

/* ===== 页头 ===== */
.page-header {
    height: 40px;
    display: flex;
    align-items: center;
    border-bottom: 1px solid #eaeaea;
    margin-bottom: 12px;
    margin-left: 24px;
}

.page-header .back {
    color: #1677ff;
    cursor: pointer;
    font-size: 16px;
}

.page-header .sep {
    margin: 0 12px;
    color: #999;
}

.page-header .title {
    font-weight: 500;
    font-size: 16px;
}

/* ===== 表单区域 ===== */
.form {
    width: 100%;
    max-width: 1200px;
}

.form :deep(.ant-form-item) {
    margin-bottom: 10px;
}

.form .row-40 :deep(.ant-form-item-control) {
    line-height: 40px;
}

/* ===== 索引映射/接口卡片 ===== */
.vector-card {
    border: 1px solid #d9d9d9;
    padding: 12px 12px 4px;
    margin: 8px 0 16px 140px;
    border-radius: 2px;
    width: calc(100% - 140px - 24px);
}

.vector-card .inner :deep(.ant-form-item-label) {
    width: 120px;
}

.vector-card .inner :deep(.ant-form-item-control) {
    line-height: 40px;
}

.api-card {
    width: calc(100% - 24px);
    border: 1px solid #d9d9d9;
    border-radius: 2px;
    background: #fff;
    padding: 12px;
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.api-card-preview {
    overflow: hidden;
}

.api-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    background: #f6f7fb;
    border-radius: 2px;
    padding: 10px 12px;
}

.api-text {
    font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
    font-size: 13px;
    white-space: normal;
    word-break: break-all;
    overflow: visible;
}

.api-ops :deep(.ant-btn) {
    padding: 0 6px;
}

.index-table-card {
    margin-left: 140px;
    width: calc(100% - 140px - 24px);
    border: 1px solid #d9d9d9;
    border-radius: 2px;
    background: #fff;
    overflow: hidden;
    margin-bottom: 12px;
}

.index-table-card .index-table-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 6px 10px;
    background: #eef3ff;
}

.index-table-card .index-table-head .name {
    font-weight: 600;
}

.index-table-card .index-table-head .close {
    color: #666;
}

.index-table-card .mapping-table :deep(.ant-table-thead > tr > th) {
    background-color: #F4F8FD !important;
    font-weight: 500;
}

.lexicon-chips {
    margin-left: 140px;
}

/* 删除按钮 */
.del-btn {
    padding: 0 6px;
    color: #ff4d4f;
}

/* 多选下拉换行 */
.multi-wrap :deep(.ant-select-selector) {
    min-height: 32px;
    height: auto !important;
    line-height: normal !important;
    white-space: normal;
    align-items: flex-start;
    padding-top: 2px;
    padding-bottom: 2px;
}

.multi-wrap :deep(.ant-select-selection-overflow) {
    flex-wrap: wrap;
}

.multi-wrap :deep(.ant-select-selection-item) {
    height: 24px;
    line-height: 24px;
}

.mapping-table :deep(.ant-table-tbody > tr > td) {
    padding: 6px 10px;
    vertical-align: top;
}

.cell-stretch {
    width: 100%;
}

.cell-stretch :deep(.ant-input),
.cell-stretch :deep(.ant-select) {
    width: 100%;
}

:deep(.ant-select-dropdown) {
    max-width: 560px;
}

.empty-select {
    min-height: 32px;
    padding: 0 8px;
    border: 1px dashed #d9d9d9;
    background: #fafafa;
    border-radius: 2px;
    color: #999;
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.empty-select .tip {
    font-size: 13px;
}

.index-add-row {
    border-top: 1px solid #eee;
}

.index-add-row .add-btn {
    text-align: center;
    background: #e6efff;
    color: #3b6cff;
    padding: 10px 0;
    user-select: none;
}

:deep(.ant-table-thead > tr > th) {
    background-color: #F4F8FD !important;
}

.page-actions {
    width: 100%;
    display: flex;
    justify-content: center;
    gap: 12px;
    margin: 18px 0 8px;
}

.modal-narrow :deep(.ant-modal) {
    max-width: 92vw;
}

@media (max-width: 900px) {

    .index-table-card,
    .algo-picked-wrap,
    .lexicon-chips,
    .vector-card,
    .api-card {
        margin-left: 0;
        width: 100%;
    }
}

mapping-table :deep(.ant-table-tbody > tr > td:nth-child(3)) {
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
}

/* ===== 预览弹窗 ===== */
.api-row-block {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 10px 12px;
    border: 1px solid #eef0f4;
    border-radius: 6px;
    background: #fff;
}

.api-row-block+.api-row-block {
    margin-top: 12px;
}

.api-type-line {
    display: flex;
    align-items: center;
    gap: 6px;
}

.api-type-line .label,
.api-params .label {
    color: #666;
    font-size: 13px;
}

.api-params .params-pre {
    margin: 6px 0 0 0;
    padding: 10px 12px;
    background: #f8f9fb;
    border: 1px solid #eef0f4;
    border-radius: 4px;
    font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
    font-size: 13px;
    line-height: 1.5;
    white-space: pre-wrap;
    word-break: break-all;
}

.algo-picked-wrap {
    margin-left: 140px;
    padding: 8px 0 4px;
}

.algo-picked-row {
    display: grid;
    grid-template-columns: max-content 1fr max-content;
    align-items: center;
    gap: 8px;
    margin-bottom: 10px;
}

.algo-picked-row :deep(.ant-select) {
    width: 100%;
}

.lexicon-chips {
    margin-left: 140px;
    width: calc(100% - 140px - 24px);
    border: 1px solid #d9d9d9;
    padding: 10px 12px;
    border-radius: 4px;
    background: #fff;
    margin-bottom: 12px;
}

.lexicon-chips .chip {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    background: #e6ecff;
    color: #3b6cff;
    padding: 6px 10px;
    border-radius: 4px;
    margin-right: 12px;
    margin-bottom: 8px;
    font-size: 14px;
}


.form :deep(.ant-input[disabled]),
.form :deep(.ant-input-number-disabled .ant-input-number-input),
.form :deep(.ant-select-disabled .ant-select-selector),
.form :deep(.ant-radio-disabled + span),
.form :deep(.ant-btn[disabled]) {
    background: #fff !important;
    color: #333 !important;
    opacity: 1;
    cursor: not-allowed !important;
}

.del-btn[disabled] {
    color: #ff4d4f !important;
    opacity: .6;
    cursor: not-allowed;
}

.form :deep(.ant-select-disabled .ant-select-selector) {
    border-color: #d9d9d9 !important;
}

.form :deep(.ant-select-disabled .ant-select-arrow) {
    opacity: 1;
}

/* 向量启停按钮禁用态 */
.vector-toggle[disabled] {
    opacity: 1;
}

.vector-toggle.ant-btn-background-ghost[disabled] {
    background: #fff !important;
    border-color: #1677ff !important;
    color: #1677ff !important;
}

.vector-toggle[disabled]:not(.ant-btn-background-ghost) {
    background: #1677ff !important;
    border-color: #1677ff !important;
    color: #fff !important;
}

.active-on[disabled] {}

.active-off[disabled] {}
</style>
